﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Messaging;

using System.Reflection;
using MessageCallAspect.Inspector.Request;
using MessageCallAspect.Inspector.Response;
using MessageCallAspect.Inspector.MessageSink;
using System.Diagnostics;
using MessageCallAspect.Aspect.Log;
using Syk.Library.Extensions;
using Syk.Library.Log;

namespace MessageCallAspect.Aspect.Trace
{
    [Flags]
    public enum TraceAspectCondition
    {
        BeforeInvoke = 2,
        AfterInvoke = 4,
        Parameters = 8
    }

    [DebuggerStepThrough]
    public class TraceMessageSink : SimpleMessageInspector
    {
        #region fields
        private readonly TraceAspectCondition m_TraceAspectCondition;
        #endregion

        #region ctor

        public TraceMessageSink(IServiceProvider context, object target, IMessageSink nextSink)
            : this(context, TraceAspectCondition.BeforeInvoke | TraceAspectCondition.AfterInvoke, target, nextSink)
        {
        }

        public TraceMessageSink(IServiceProvider context, TraceAspectCondition traceAspectCondition, object target, IMessageSink nextSink)
            : base(context, target, nextSink)
        {
            m_TraceAspectCondition = traceAspectCondition;
        }

        #endregion

        protected override void OnBeforeInvoke(IMessageRequest request)
        {
            base.OnBeforeInvoke(request);
            if ((m_TraceAspectCondition & TraceAspectCondition.BeforeInvoke) > 0)
            {
                StringBuilder sb = new StringBuilder();
                if ((m_TraceAspectCondition & TraceAspectCondition.Parameters) > 0)
                {
                    sb.AppendFormat("TraceAspect - Before invoke : {0}{1}.{2}(", request.Method.ReturnParameter, request.Target.GetType().Name, request.Method.Name);
                    // Loop through the [in] parameters
                    ParameterInfo[] parameters = request.Method.GetParameters();
                    for (int i = 0; i < parameters.Length; ++i)
                    {
                        if (i > 0)
                            sb.Append(", ");

                        sb.AppendFormat("{0} = {1}", parameters[i].Name, request.Arguments[i].DumpToString(10));
                    }
                    sb.Append(")");
                }
                else
                {
                    sb.AppendFormat("TraceAspect - Before invoke : {0}.{1}", request.Target.GetType().Name, request.Method.Name);
                }
                Logger.Instance.Debug(request.Target.GetType().Name, sb.ToString());
            }
        }

        protected override void OnAfterInvoke(IMessageRequest request, IMessageResponse response)
        {
            base.OnAfterInvoke(request, response);
            if ((m_TraceAspectCondition & TraceAspectCondition.AfterInvoke) > 0)
            {
                StringBuilder sb = new StringBuilder();
                if ((m_TraceAspectCondition & TraceAspectCondition.Parameters) > 0)
                {
                    sb.AppendFormat("TraceAspect - After invoke took {3} sec : {0}{1}.{2}(", request.Method.ReturnParameter, request.Target.GetType().Name, request.Method.Name, response.Duration.TotalSeconds);
                    //Loop through all the [out] parameters
                    ParameterInfo[] parameters = request.Method.GetParameters();

                    int outParamIndex = 0;
                    for (int i = 0; i < parameters.Length; ++i)
                    {
                        if (i > 0)
                            sb.Append(", ");

                        if (parameters[i].IsOut && response.OutArgs.Length > outParamIndex)
                            sb.AppendFormat("<out>{0} = {1}", parameters[i].Name, response.OutArgs[outParamIndex++].DumpToString(10));
                        else
                            sb.AppendFormat("{0} = {1}", parameters[i].Name, request.Arguments[i].DumpToString(10));
                    }

                    if (response.ReturnValue != null && response.ReturnValue.GetType() != typeof(void))
                        sb.AppendFormat(" returned [{0}]", response.ReturnValue.DumpToString(10));

                    if (response.Exception != null)
                        sb.AppendFormat("Exception was thrown: {0}", response.Exception);

                    sb.Append(")");
                }
                else
                {
                    sb.AppendFormat("TraceAspect - After invoke  : {0}.{1} took {2} sec", request.Target.GetType().Name, request.Method.Name, response.Duration.TotalSeconds);
                }
                sb.Append("\n");

                //sb.AppendFormat(") took {0} seconds \n", response.Duration.TotalSeconds);
                Logger.Instance.Debug(request.Target.GetType().Name, sb.ToString());
            }

        }
    }
}
